﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Web.Mvc;

namespace Mefisto4.Core.MEF
{
    public class MVCCatalog : ComposablePartCatalog, ICompositionElement
    {
        private readonly Type[] _types;
        private readonly object _locker = new object();
        private IQueryable<ComposablePartDefinition> _parts;

        public MVCCatalog(params Type[] types)
        {
            _types = types;
        }

        public MVCCatalog(Assembly assembly)
        {
            try
            {
                _types = assembly.GetTypes();
            }
            catch (ReflectionTypeLoadException typeLoadException)
            {
                _types = typeLoadException.Types;
            }
        }

        public MVCCatalog(string dir)
        {
            var fileSet = new List<Type>();

            foreach (var fileName in Directory.GetFiles(dir, "*.dll", SearchOption.AllDirectories))
            {
                var assembly = Assembly.LoadFrom(fileName);

                try
                {
                    var asmCat = new AssemblyCatalog(assembly);

                    //Force MEF to load the plugin and figure out if there are any exports
                    // good assemblies will not throw the RTLE exception and can be added to the catalog
                    if (asmCat.Parts.Any())
                        fileSet.AddRange(assembly.GetExportedTypes());
                }
                catch (ReflectionTypeLoadException)
                {
                }
            }

            _types = fileSet.ToArray();
        }

        public override IQueryable<ComposablePartDefinition> Parts
        {
            get { return InternalParts; }
        }

        #region Implementation of ICompositionElement

        string ICompositionElement.DisplayName
        {
            get { return "MVCCatalog"; }
        }

        ICompositionElement ICompositionElement.Origin
        {
            get { return null; }
        }

        #endregion

        internal IQueryable<ComposablePartDefinition> InternalParts
        {
            get
            {
                if (_parts == null)
                {
                    lock (_locker)
                    {
                        if (_parts == null)
                        {
                            var partsCollection = new List<ComposablePartDefinition>();

                            foreach (var type in _types)
                            {
                                var typeCatalog = new TypeCatalog(type);

                                var part = typeCatalog.Parts.FirstOrDefault();

                                if (part == null)
                                    continue;

                                if (typeof(IController).IsAssignableFrom(type))
                                {
                                    part = new ControllerPartDefinitionDecorator(part, type, type.Name, type.Namespace);
                                }

                                partsCollection.Add(part);
                            }

                            Thread.MemoryBarrier();

                            _parts = partsCollection.AsQueryable();
                        }
                    }
                }

                return _parts;
            }
        }
    }
}